package websockets

import (
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
	"gopkg.in/fatih/set.v0"
	"log"
	"luna/enum/code"
	"luna/enum/fields"
	"luna/models"
	response "luna/response"
	"net/http"
	"strconv"
	"sync"
)

var (
	// ClientMap 用户id跟连接资源关系
	ClientMap map[int64]*Node = make(map[int64]*Node, 0)
	// RWLocker 读写锁
	RWLocker sync.RWMutex
)

// Node 连接节点保存
type Node struct {
	userId     int64
	Conn       *websocket.Conn
	DataQueue  chan []byte
	GroupSets  set.Interface
	tcpAddress []string
}

func HandleDoing(c *gin.Context) {
	var (
		token    string
		idString string
		id       int64
		isAllow  bool
		conn     *websocket.Conn
		err      error
	)

	token = c.Query("token")
	if token == "" {
		c.JSON(http.StatusOK, response.Response(code.ParameterError, "token为空", ""))
		return
	}

	idString = c.Query("id")
	if idString == "" {
		c.JSON(http.StatusOK, response.Response(code.ParameterError, "id为空", ""))
		return
	}
	if id, err = strconv.ParseInt(idString, 10, 64); err != nil {
		c.JSON(http.StatusOK, response.Response(code.ParameterError, "id有误", ""))
		return
	}

	isAllow = true
	//允许跨域 并返回是否可以连接
	conn, err = (&websocket.Upgrader{
		CheckOrigin: func(r *http.Request) bool {
			return isAllow
		},
	}).Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		log.Println(err.Error())
		c.JSON(http.StatusOK, response.Response(code.SocketError, err.Error(), "socket连接错误"))
		return
	}

	//做用户id跟socket资源的映射
	node := &Node{
		userId:    id,
		Conn:      conn,
		DataQueue: make(chan []byte, 50),
		GroupSets: set.New(set.ThreadSafe),
	}

	RWLocker.Lock()
	ClientMap[id] = node
	RWLocker.Unlock()

	//发送数据
	go SendGoroutines(node)
	//接收数据
	go ReceiveGoroutines(node)
}

// SendGoroutines 发送协程
func SendGoroutines(node *Node) {
	for {
		select {
		case data := <-node.DataQueue:
			err := node.Conn.WriteMessage(websocket.TextMessage, data)
			if err != nil {
				//当发送信息时，接受信息的对象断开后会接到err报错：websocket: close sent
				//删除内存中的其中map
				RWLocker.Lock()
				delete(ClientMap, node.userId)
				RWLocker.Unlock()
				log.Println("SendGoroutines 发送信号")
				log.Println(err.Error())
				return
			}
		}
	}
}

// ReceiveGoroutines 接收协程
func ReceiveGoroutines(node *Node) {
	for {
		_, data, err := node.Conn.ReadMessage()
		if err != nil {
			//当客户端断开时可以此处会err报：websocket: close 1005 (no status)
			RWLocker.Lock()
			delete(ClientMap, node.userId)
			RWLocker.Unlock()
			log.Println("ReceiveGoroutines 接收信号")
			log.Println(err.Error())
			return
		}

		fmt.Printf("recv<=%s", data)
		fmt.Println("----|||")
		//获取接收消息用户的id
		Dispatch(data)
	}
}

// SendMessage 发送消息
func SendMessage(userId int64, msg []byte) {
	RWLocker.RLock()
	node, ok := ClientMap[userId]
	RWLocker.RUnlock()
	if ok {
		node.DataQueue <- msg
	}
}

// Dispatch  解密出接收的消息跟接收消息的用户
func Dispatch(data []byte) {
	// websocket是message单位不需要做粘包处理,tcp需要做粘包处理

	msg := models.Message{}
	err := json.Unmarshal(data, &msg)
	if err != nil {
		fmt.Println("----")
		log.Println(err.Error())
		fmt.Println("----")
		return
	}

	switch msg.Cmd {
	case fields.CMD_SINGLE_MSG:
		//分布式发送消息处理
		SendMessage(msg.DstId, data)
	}
}
